<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\ORM\Mapping\Driver;

use Doctrine\ORM\Mapping\ClassMetadataInfo, Doctrine\ORM\Mapping\MappingException;

/**
 * The StaticPHPDriver calls a static loadMetadata() method on your entity
 * classes where you can manually populate the ClassMetadata instance.
 *
 * @license 	http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @link    	www.doctrine-project.org
 * @since   	2.0
 * @author      Benjamin Eberlei <kontakt@beberlei.de>
 * @author      Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author      Jonathan H. Wage <jonwage@gmail.com>
 * @author      Roman Borschel <roman@code-factory.org>
 */
class StaticPHPDriver implements Driver {
	/**
	 * Paths of entity directories.
	 * 
	 * @var array
	 */
	private $_paths = array();
	
	/**
	 * Map of all class names.
	 * 
	 * @var array
	 */
	private $_classNames;
	
	/**
	 * The file extension of mapping documents.
	 * 
	 * @var string
	 */
	private $_fileExtension = '.php';
	
	public function __construct($paths) {
		$this->addPaths( ( array ) $paths );
	}
	
	public function addPaths(array $paths) {
		$this->_paths = array_unique( array_merge( $this->_paths, $paths ) );
	}
	
	/**
	 * {@inheritdoc}
	 */
	public function loadMetadataForClass($className, ClassMetadataInfo $metadata) {
		call_user_func_array( array( $className, 'loadMetadata' ), array( $metadata ) );
	}
	
	/**
	 * {@inheritDoc}
	 * @todo Same code exists in AnnotationDriver, should we re-use it somehow or not worry about it?
	 */
	public function getAllClassNames() {
		if( $this->_classNames !== null ) {
			return $this->_classNames;
		}
		
		if( !$this->_paths ) {
			throw MappingException::pathRequired();
		}
		
		$classes = array();
		$includedFiles = array();
		
		foreach( $this->_paths as $path ) {
			if( !is_dir( $path ) ) {
				throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath( $path );
			}
			
			$iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $path ), \RecursiveIteratorIterator::LEAVES_ONLY );
			
			foreach( $iterator as $file ) {
				if( ($fileName = $file->getBasename( $this->_fileExtension )) == $file->getBasename() ) {
					continue;
				}
				
				$sourceFile = realpath( $file->getPathName() );
				require_once $sourceFile;
				$includedFiles[] = $sourceFile;
			}
		}
		
		$declared = get_declared_classes();
		
		foreach( $declared as $className ) {
			$rc = new \ReflectionClass( $className );
			$sourceFile = $rc->getFileName();
			if( in_array( $sourceFile, $includedFiles ) && !$this->isTransient( $className ) ) {
				$classes[] = $className;
			}
		}
		
		$this->_classNames = $classes;
		
		return $classes;
	}
	
	/**
	 * {@inheritdoc}
	 */
	public function isTransient($className) {
		return method_exists( $className, 'loadMetadata' ) ? false : true;
	}
}
